﻿using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System;
using AuchanScan.Animations;

namespace AuchanScan.Interactions
{
    /// <summary>
    /// Animates an element so that it flies out and flies in!
    /// </summary>
    public class ContinuumAnimation
    {
        #region IsContinuumItem (Attached DependencyProperty)

        public static readonly DependencyProperty IsContinuumItemProperty =
            DependencyProperty.RegisterAttached("IsContinuumItem", typeof(bool), typeof(ContinuumAnimation), new PropertyMetadata(false));

        public static void SetIsContinuumItem(DependencyObject o, bool value)
        {
            o.SetValue(IsContinuumItemProperty, value);
        }

        public static bool GetIsContinuumItem(DependencyObject o)
        {
            return (bool)o.GetValue(IsContinuumItemProperty);
        }

        #endregion

        private PhoneApplicationFrame rootVisual
        {
            get { return Application.Current.RootVisual as PhoneApplicationFrame; }
        }

        private Brush _transitionBackground;
        public Brush TransitionBackground
        {
            get
            {
                return _transitionBackground;
            }

            set
            {
                if (_transitionBackground != value)
                {
                    _transitionBackground = value;
                    OnTransitionBackgroundChanged();
                }
            }
        }

        private Popup _popup;
        private Canvas _popupCanvas;

        private FrameworkElement _continuumElement;
        private Point _continuumElementPosition;
        private Image _continuumElementClone;
        private double _continuumElementOpacity = 1.0;

        private Rectangle _backgroundMask;

        private static double _flyInSpeed = 0.3;
        /// <summary>
        /// Fly in duration in seconds
        /// </summary>
        public static double FlyInDuration { get { return _flyInSpeed; } }

        private static double _flyOutSpeed = 0.2;
        /// <summary>
        /// Fly out duration in seconds
        /// </summary>
        public static double FlyOutDuration { get { return _flyOutSpeed; } }

        private static double _backgroundMaskSpeed = 0.1;

        private Storyboard _activeStoryboard;

        /// <summary>
        /// Constructor
        /// </summary>
        public ContinuumAnimation()
        {
            InitializePopup();

            // Default transition background from the phone
            TransitionBackground = Application.Current.Resources["PhoneBackgroundBrush"] as Brush;
        }

        private void InitializePopup()
        {
            // Construct a popup, with a Canvas as its child
            _popup = new Popup();
            _popup.CacheMode = new BitmapCache();
            _popupCanvas = new Canvas();            
            _popup.Child = _popupCanvas;

            _backgroundMask = new Rectangle()
            {
                Opacity = 1,
                Width = rootVisual.ActualWidth,
                Height = rootVisual.ActualHeight,
                Fill = TransitionBackground
            };

            _popupCanvas.Children.Add(_backgroundMask);
        }

        private void OnTransitionBackgroundChanged()
        {
            if (_backgroundMask != null)
                _backgroundMask.Fill = TransitionBackground;
        }

        public void SkipAnimation()
        {
            if (_activeStoryboard != null)
            {
                _activeStoryboard.SkipToFill();
                _popup.IsOpen = false;
            }
        }

        #region Detail

        /// <summary>
        /// Animate the continuum element to the detail page
        /// </summary>
        /// <param name="title"></param>
        public void DetailItemFlyIn(FrameworkElement title, Action onCompleted = null)
        {
            if (title == null)
                return;

            _continuumElement = title;

            _backgroundMask.Opacity = 1;

            // Open the popup
            _popup.IsOpen = true;

            // Hide the element we are going to 'clone' into the popup
            _continuumElementOpacity = _continuumElement.Opacity;
            _continuumElement.Opacity = 0.0;

            // The dispatcher is needed here, to get the title a chance 
            // to render before trying to take the snapshot
            _continuumElement.Loaded += (sender, args) =>
            {
                // Make the target element visible, just for the time of the snapshot
                _continuumElement.Opacity = _continuumElementOpacity;

                _continuumElementClone = GenerateScreenshot(_continuumElement);

                _popupCanvas.Children.Add(_continuumElementClone);

                // ... and hidden again !
                _continuumElement.Opacity = 0.0;

                var sb = new Storyboard();

                _continuumElementPosition = _continuumElement.GetRelativePosition(rootVisual);
                Canvas.SetTop(_continuumElementClone,
                              _continuumElementPosition.Y - 100);
                Canvas.SetLeft(_continuumElementClone,
                               _continuumElementPosition.X + 300);

                // Animate the X position
                var db = Animator.AddEasingDoubleAnimation(
                    _continuumElementPosition.X + 300,
                    _continuumElementPosition.X,
                    _flyInSpeed,
                    new SineEase(),
                    Canvas.LeftProperty,
                    _continuumElementClone);
                sb.Children.Add(db);

                // Animate the Y position
                db = Animator.AddEasingDoubleAnimation(
                    _continuumElementPosition.Y - 100,
                    _continuumElementPosition.Y,
                    _flyInSpeed,
                    new SineEase(),
                    Canvas.TopProperty,
                    _continuumElementClone);
                sb.Children.Add(db);

                // Fade out the other elements
                db = Animator.AddEasingDoubleAnimation(1, 0, _flyInSpeed, null, UIElement.OpacityProperty, _backgroundMask);
                sb.Children.Add(db);

                sb.Completed += (s, e) =>
                {
                    if (onCompleted != null)
                        onCompleted();

                    ItemFlyInCompleted();
                };

                _activeStoryboard = sb;

                sb.Begin();
            };
        }

        /// <summary>
        /// Animate the detail page out when navigating back
        /// </summary>
        public void DetailItemFlyOut(Action onCompleted = null)
        {
            _popup.IsOpen = true;
            _backgroundMask.Opacity = 0.0;

            var sb = new Storyboard();

            // fade out the other elements
            var db = Animator.AddEasingDoubleAnimation(0, 1, _flyOutSpeed, null, UIElement.OpacityProperty, _backgroundMask);
            sb.Children.Add(db);

            sb.Completed += (s, e2) =>
            {
                if (onCompleted != null)
                    onCompleted();

                ItemFlyOutCompleted();
            };

            _activeStoryboard = sb;

            sb.Begin();
        }

        #endregion

        #region Master

        /// <summary>
        /// Animate the previously 'flown-out' element back to its original location
        /// </summary>
        public void MasterItemFlyIn(Action onCompleted = null)
        {
            if (_continuumElement == null || _continuumElementClone == null)
            {
                return;
            }

            // Checks wether the item is visible on screen and if not, no need to animate
            var position = _continuumElement.GetRelativePosition(rootVisual);
            if (position.X > rootVisual.ActualWidth || position.Y > rootVisual.ActualHeight)
            {
                _continuumElement.Opacity = 1.0;
                return;
            }

            _popup.IsOpen = true;
            _backgroundMask.Opacity = 1;

            var sb = new Storyboard();

            // animate the X position
            var db = Animator.AddEasingDoubleAnimation(_continuumElementPosition.X - 300, _continuumElementPosition.X, _flyInSpeed,
                new SineEase(), Canvas.LeftProperty, _continuumElementClone);
            sb.Children.Add(db);

            // animate the Y position
            db = Animator.AddEasingDoubleAnimation(_continuumElementPosition.Y - 50, _continuumElementPosition.Y, _flyInSpeed,
                new SineEase(), Canvas.TopProperty, _continuumElementClone);
            sb.Children.Add(db);

            db = Animator.AddEasingDoubleAnimation(1, 0, _flyInSpeed, null, UIElement.OpacityProperty, _backgroundMask);
            sb.Children.Add(db);

            sb.Completed += (s, e) =>
            {
                if (onCompleted != null)
                    onCompleted();

                ItemFlyInCompleted();
            };

            _activeStoryboard = sb;

            sb.Begin();
        }

        /// <summary>
        /// Animate the given element so that it flies off screen, fading 
        /// everything else that is on screen.
        /// </summary>
        public void MasterItemFlyOut(FrameworkElement item, Action onCompleted = null)
        {
            //var item = GetContinuumElement();
            if (item == null)
                return;

            _continuumElement = item;

            _backgroundMask.Opacity = 0.0;

            _continuumElementClone = GenerateScreenshot(_continuumElement);
            _popupCanvas.Children.Add(_continuumElementClone);

            _continuumElementPosition = _continuumElement.GetRelativePosition(rootVisual);
            Canvas.SetTop(_continuumElementClone, _continuumElementPosition.Y);
            Canvas.SetLeft(_continuumElementClone, _continuumElementPosition.X);

            var sb = new Storyboard();

            // animate the X position
            var db = Animator.AddEasingDoubleAnimation(_continuumElementPosition.X, _continuumElementPosition.X + 500, _flyOutSpeed,
                new SineEase() { EasingMode = EasingMode.EaseIn }, Canvas.LeftProperty, _continuumElementClone);
            sb.Children.Add(db);

            // animate the Y position
            db = Animator.AddEasingDoubleAnimation(_continuumElementPosition.Y, _continuumElementPosition.Y + 50, _flyOutSpeed,
                new SineEase() { EasingMode = EasingMode.EaseOut }, Canvas.TopProperty, _continuumElementClone);
            sb.Children.Add(db);

            // fade out the other elements
            db = Animator.AddEasingDoubleAnimation(0, 1, _flyOutSpeed, null, UIElement.OpacityProperty, _backgroundMask);
            sb.Children.Add(db);

            sb.Completed += (s, e2) =>
            {
                if (onCompleted != null)
                    onCompleted();

                ItemFlyOutCompleted();
            };

            // hide the element we have 'cloned' into the popup
            _continuumElementOpacity = _continuumElement.Opacity;
            _continuumElement.Opacity = 0.0;

            // open the popup
            _popup.IsOpen = true;

            _activeStoryboard = sb;

            // begin the animation
            sb.Begin();
        }

        #endregion

        #region Common

        private void ItemFlyInCompleted()
        {
            _activeStoryboard = null;

            // When the animation has finished, hide the popup
            _popup.IsOpen = false;

            // Restore the element we have animated
            _continuumElement.Opacity = _continuumElementOpacity;

            // And get rid of our clone
            _popupCanvas.Children.Remove(_continuumElementClone);
        }

        private void ItemFlyOutCompleted()
        {
            _activeStoryboard = null;

            _popup.IsOpen = false;
        }

        private Image GenerateScreenshot(FrameworkElement element)
        {
            WriteableBitmap bitmap = null;

            try
            {
                bitmap = new WriteableBitmap(element, null);
            }
            catch (Exception ex)
            {
                return new Image();
            }

            bitmap.Invalidate();

            var screenshot = new Image()
            {
                Source = bitmap,
                CacheMode = new BitmapCache()
            };

            return screenshot;
        }

        #endregion
    }


    public enum ContinuumTransitionMode
    {
        Master,
        Detail
    }
}

//using System.Windows;
//using System.Windows.Controls;
//using System.Windows.Controls.Primitives;
//using System.Windows.Media;
//using System.Windows.Media.Animation;
//using System.Windows.Media.Imaging;
//using System.Windows.Shapes;
//using Microsoft.Phone.Controls;
//using Store.WP7.Framework.Animations;
//using System;

//namespace Store.WP7.Framework.Interactions
//{
//    /// <summary>
//    /// Animates an element so that it flies out and flies in!
//    /// </summary>
//    public class ContinuumAnimation
//    {
//        #region IsContinuumItem (Attached DependencyProperty)

//        public static readonly DependencyProperty IsContinuumItemProperty =
//            DependencyProperty.RegisterAttached("IsContinuumItem", typeof(bool), typeof(ContinuumAnimation), new PropertyMetadata(false));

//        public static void SetIsContinuumItem(DependencyObject o, bool value)
//        {
//            o.SetValue(IsContinuumItemProperty, value);
//        }

//        public static bool GetIsContinuumItem(DependencyObject o)
//        {
//            return (bool)o.GetValue(IsContinuumItemProperty);
//        }

//        #endregion

//        private PhoneApplicationFrame rootVisual
//        {
//            get { return Application.Current.RootVisual as PhoneApplicationFrame; }
//        }

//        private Brush _transitionBackground;
//        public Brush TransitionBackground
//        {
//            get
//            {
//                return _transitionBackground;
//            }

//            set
//            {
//                if (_transitionBackground != value)
//                {
//                    _transitionBackground = value;
//                    OnTransitionBackgroundChanged();
//                }
//            }
//        }

//        private Popup _popup;
//        private Grid _popupGrid;

//        private FrameworkElement _continuumElement;
//        private Point _continuumElementPosition;
//        private Image _continuumElementClone;
//        private double _continuumElementOpacity = 1.0;

//        private Rectangle _backgroundMask;

//        private static double _flyInSpeed = 0.3;
//        /// <summary>
//        /// Fly in duration in seconds
//        /// </summary>
//        public static double FlyInDuration { get { return _flyInSpeed; } }

//        private static double _flyOutSpeed = 0.2;
//        /// <summary>
//        /// Fly out duration in seconds
//        /// </summary>
//        public static double FlyOutDuration { get { return _flyOutSpeed; } }

//        private Storyboard _activeStoryboard;

//        /// <summary>
//        /// Constructor
//        /// </summary>
//        public ContinuumAnimation()
//        {
//            InitializePopup();

//            // Default transition background from the phone
//            TransitionBackground = Application.Current.Resources["PhoneBackgroundBrush"] as Brush;
//        }

//        private void InitializePopup()
//        {
//            // Construct a popup, with a Canvas as its child
//            _popup = new Popup();
//            _popupGrid = new Grid();
//            _popupGrid.CacheMode = new BitmapCache();
//            _popup.Child = _popupGrid;

//            _backgroundMask = new Rectangle()
//            {
//                Opacity = 1,
//                Fill = TransitionBackground
//            };

//            _popupGrid.Children.Add(_backgroundMask);
//        }

//        private void OnTransitionBackgroundChanged()
//        {
//            if (_backgroundMask != null)
//                _backgroundMask.Fill = TransitionBackground;
//        }

//        public void SkipAnimation()
//        {
//            if (_activeStoryboard != null)
//            {
//                _activeStoryboard.SkipToFill();
//                _popup.IsOpen = false;
//            }
//        }

//        #region Detail

//        /// <summary>
//        /// Animate the continuum element to the detail page
//        /// </summary>
//        /// <param name="title"></param>
//        public void DetailItemFlyIn(FrameworkElement title, Action onCompleted = null)
//        {
//            if (title == null)
//                return;

//            _continuumElement = title;

//            _backgroundMask.Opacity = 1;

//            // Open the popup
//            _popup.IsOpen = true;

//            // Hide the element we are going to 'clone' into the popup
//            _continuumElementOpacity = _continuumElement.Opacity;
//            _continuumElement.Opacity = 0.0;

//            // The dispatcher is needed here, to get the title a chance 
//            // to render before trying to take the snapshot
//            _continuumElement.Loaded += (sender, args) =>
//            {
//                // Make the target element visible, just for the time of the snapshot
//                _continuumElement.Opacity = _continuumElementOpacity;

//                _continuumElementClone = GenerateScreenshot(_continuumElement);
//                var translate = new TranslateTransform();
//                _continuumElementClone.RenderTransform = translate;

//                _popupGrid.Children.Add(_continuumElementClone);

//                // ... and hidden again !
//                _continuumElement.Opacity = 0.0;

//                var sb = new Storyboard();

//                _continuumElementPosition = _continuumElement.GetRelativePosition(rootVisual);
//                translate.Y = _continuumElementPosition.Y - 100;
//                translate.X = _continuumElementPosition.X + 300;

//                // Animate the X position
//                var db = Animator.AddEasingDoubleAnimation(
//                    _continuumElementPosition.X + 300,
//                    _continuumElementPosition.X,
//                    _flyInSpeed,
//                    new SineEase(),
//                    TranslateTransform.XProperty,
//                    translate);
//                sb.Children.Add(db);

//                // Animate the Y position
//                db = Animator.AddEasingDoubleAnimation(
//                    _continuumElementPosition.Y - 100,
//                    _continuumElementPosition.Y,
//                    _flyInSpeed,
//                    new SineEase(),
//                    TranslateTransform.YProperty,
//                    translate);
//                sb.Children.Add(db);

//                // Fade out the other elements
//                db = Animator.AddEasingDoubleAnimation(1, 0, _flyOutSpeed, null, UIElement.OpacityProperty, _backgroundMask);
//                sb.Children.Add(db);

//                sb.Completed += (s, e) =>
//                {
//                    if (onCompleted != null)
//                        onCompleted();

//                    ItemFlyInCompleted();
//                };

//                _activeStoryboard = sb;

//                sb.Begin();
//            };
//        }

//        /// <summary>
//        /// Animate the detail page out when navigating back
//        /// </summary>
//        public void DetailItemFlyOut(Action onCompleted = null)
//        {
//            _popup.IsOpen = true;

//            _backgroundMask.Opacity = 0.0;

//            var sb = new Storyboard();

//            // fade out the other elements
//            var db = Animator.AddEasingDoubleAnimation(0, 1, _flyOutSpeed, null, UIElement.OpacityProperty, _backgroundMask);
//            sb.Children.Add(db);

//            sb.Completed += (s, e2) =>
//            {
//                if (onCompleted != null)
//                    onCompleted();

//                ItemFlyOutCompleted();
//            };

//            _activeStoryboard = sb;

//            sb.Begin();
//        }

//        #endregion

//        #region Master

//        /// <summary>
//        /// Animate the previously 'flown-out' element back to its original location
//        /// </summary>
//        public void MasterItemFlyIn(Action onCompleted = null)
//        {
//            if (_continuumElement == null || _continuumElementClone == null)
//            {
//                return;
//            }

//            // Checks wether the item is visible on screen and if not, no need to animate
//            var position = _continuumElement.GetRelativePosition(rootVisual);
//            if (position.X > rootVisual.ActualWidth || position.Y > rootVisual.ActualHeight)
//            {
//                _continuumElement.Opacity = 1.0;
//                return;
//            }

//            _popup.IsOpen = true;
//            _backgroundMask.Opacity = 1;

//            var sb = new Storyboard();

//            // animate the X position
//            var db = Animator.AddEasingDoubleAnimation(_continuumElementPosition.X - 300, _continuumElementPosition.X, _flyInSpeed,
//                new SineEase(), TranslateTransform.XProperty, _continuumElementClone.RenderTransform);
//            sb.Children.Add(db);

//            // animate the Y position
//            db = Animator.AddEasingDoubleAnimation(_continuumElementPosition.Y - 50, _continuumElementPosition.Y, _flyInSpeed,
//                new SineEase(), TranslateTransform.YProperty, _continuumElementClone.RenderTransform);
//            sb.Children.Add(db);

//            db = Animator.AddEasingDoubleAnimation(1, 0, _flyInSpeed, null, UIElement.OpacityProperty, _backgroundMask);
//            sb.Children.Add(db);

//            sb.Completed += (s, e) =>
//            {
//                if (onCompleted != null)
//                    onCompleted();

//                ItemFlyInCompleted();
//            };

//            _activeStoryboard = sb;

//            sb.Begin();
//        }

//        /// <summary>
//        /// Animate the given element so that it flies off screen, fading 
//        /// everything else that is on screen.
//        /// </summary>
//        public void MasterItemFlyOut(FrameworkElement item, Action onCompleted = null)
//        {
//            //var item = GetContinuumElement();
//            if (item == null)
//                return;

//            _continuumElement = item;

//            _backgroundMask.Opacity = 0.0;

//            _continuumElementClone = GenerateScreenshot(_continuumElement);
//            var translate = new TranslateTransform();
//            _continuumElementClone.RenderTransform = translate;

//            _popupGrid.Children.Add(_continuumElementClone);

//            _continuumElementPosition = _continuumElement.GetRelativePosition(rootVisual);
//            translate.Y = _continuumElementPosition.Y;
//            translate.X = _continuumElementPosition.X;

//            var sb = new Storyboard();

//            // animate the X position
//            var db = Animator.AddEasingDoubleAnimation(_continuumElementPosition.X, _continuumElementPosition.X + 500, _flyOutSpeed,
//                new SineEase() { EasingMode = EasingMode.EaseIn }, TranslateTransform.XProperty, translate);
//            sb.Children.Add(db);

//            // animate the Y position
//            db = Animator.AddEasingDoubleAnimation(_continuumElementPosition.Y, _continuumElementPosition.Y + 50, _flyOutSpeed,
//                new SineEase() { EasingMode = EasingMode.EaseOut }, TranslateTransform.YProperty, translate);
//            sb.Children.Add(db);

//            // fade out the other elements
//            db = Animator.AddEasingDoubleAnimation(0, 1, _flyOutSpeed, null, UIElement.OpacityProperty, _backgroundMask);
//            sb.Children.Add(db);

//            sb.Completed += (s, e2) =>
//            {
//                if (onCompleted != null)
//                    onCompleted();

//                ItemFlyOutCompleted();
//            };

//            // hide the element we have 'cloned' into the popup
//            _continuumElementOpacity = _continuumElement.Opacity;
//            _continuumElement.Opacity = 0.0;

//            // open the popup
//            _popup.IsOpen = true;

//            _activeStoryboard = sb;

//            // begin the animation
//            sb.Begin();
//        }

//        #endregion

//        #region Common

//        private void ItemFlyInCompleted()
//        {
//            _activeStoryboard = null;

//            // When the animation has finished, hide the popup
//            _popup.IsOpen = false;

//            // Restore the element we have animated
//            _continuumElement.Opacity = _continuumElementOpacity;

//            // And get rid of our clone
//            _popupGrid.Children.Remove(_continuumElementClone);
//        }

//        private void ItemFlyOutCompleted()
//        {
//            _activeStoryboard = null;

//            _popup.IsOpen = false;
//        }

//        private Image GenerateScreenshot(FrameworkElement element)
//        {
//            WriteableBitmap bitmap = null;

//            try
//            {
//                bitmap = new WriteableBitmap((int)rootVisual.ActualWidth, (int)rootVisual.ActualHeight);
//                bitmap.Render(element, null);
//            }
//            catch (Exception ex)
//            {
//                return new Image();
//            }

//            bitmap.Invalidate();

//            var screenshot = new Image()
//            {
//                Source = bitmap,
//                CacheMode = new BitmapCache()
//            };

//            return screenshot;
//        }

//        #endregion
//    }


//    public enum ContinuumTransitionMode
//    {
//        Master,
//        Detail
//    }
//}